// <copyright file="PageController.cs" company="Clever Software">
// Copyright (c) 2013, 2014 All Right Reserved, http://www.cleversoftware.by
//
// This source is subject to the Microsoft Permissive License.
// Please see the License.txt file for more information.
// All other rights reserved.
//
// THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY 
// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
// PARTICULAR PURPOSE.
//
// </copyright>
// <author>Clever Software</author>
// <email>info@cleversoftware.by</email>
// <date>2014-02-04</date> 
// <summary>CleverCMS source code</summary>

namespace CleverCMS.Web.Cms.Admin.Controllers
{
    using System;
    using System.Linq;
    using System.Web.Mvc;
    using System.Collections.Generic;

    using CleverCMS.Cms.Data.Entities;
    using CleverCMS.Cms.Services;
    using CleverCMS.Cms.Services.ViewModels;
    using CleverCMS.Cms.Data.Extensions;

    using CleverCMS.Core.Services;
    using CleverCMS.Core.Web.Filters;
    using CleverCMS.Core.Extensions;
    using CleverCMS.Web.Controllers;
    

    [Authorize(Roles="Admin")]
    public class PageController : CleverCmsController
    {
        private readonly PageService        m_pageService;
        private readonly CategoryService    m_catService;
        private readonly LoggingService     m_loggingService;

        public PageController(PageService       pageService, 
                              CategoryService   categoryService, 
                              LoggingService    loggingService)
        {
            this.m_pageService = pageService;
            this.m_catService = categoryService;
            this.m_loggingService = loggingService;
        }
  
        public ViewResult List()
        {
            PageListViewModel model = new PageListViewModel()
            {
                EditPage = new AdminEditPageViewModel
                {
                    PageStatuses = GetStatuses(null),
                    Categories = GetCategories(null),
                }
            };
            return View(model);
        }

        #region Page
        [AcceptVerbs(HttpVerbs.Get)]
        public ViewResult Edit(int? id)
        {
            if (id != null)
            {
                CmsPage entry = m_pageService.GetById(id.Value);

                if (entry != null)
                    return View(new AdminEditPageViewModel { Page = entry, PageStatuses = GetStatuses(entry.StatusId), Categories = GetCategories(entry.CategoryId), PageStatusId = entry.StatusId });
                else
                    TempData["SuccessResult"] = "Items was not found!";
            }
            return View(new AdminEditPageViewModel { Page = new CmsPage(), PageStatuses = GetStatuses(null), Categories = GetCategories(null) });
        }

        [AcceptVerbs(HttpVerbs.Post)]
        public JsonResult EditPage(int? id)
        {
            if (id != null)
            {
                CmsPage entry = m_pageService.GetById(id.Value);

                if (entry != null)
                {
                    var model = new PageViewModel
                    {
                        Id = entry.Id,
                        Alias = entry.Alias,
                        AuthorId = entry.AuthorId,
                        CategoryId = entry.CategoryId,
                        CreatedAt = entry.CreatedAt.ToString(),
                        ModifiedAt = entry.ModifiedAt.ToString(),
                        HasComments = entry.HasComments,
                        MetaDescription = entry.MetaDescription,
                        MetaKeywords = entry.MetaKeywords,
                        SeoTitle = entry.SeoTitle,
                        PageContent = entry.PageContent,
                        ShowRating =  entry.ShowRating,
                        ViewCount = entry.ViewCount,
                        PageStatusId = entry.StatusId,
                        PageTitle = entry.PageTitle,
                        Tags = entry.Tags.ToTagString(),
                    };
                    
                    return Json(model);
                }
                else
                    TempData["SuccessResult"] = "Items was not found!";
            }
            return Json(null);
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateInput(false)]
        [CurrentUserFilter]
        public ActionResult Edit(AdminEditPageViewModel vm, FormCollection forms, string userName)
        {
            if (ModelState.IsValid)
            {
                var pageFromDb = m_pageService.GetById(vm.Page.Id);
                TryUpdateModel(pageFromDb, "Page", forms.ToValueProvider());
                UpdatePageStatus(pageFromDb, vm.PageStatusId, forms.ToValueProvider());
                pageFromDb.TagString = forms.GetValue("Tags");

                if (!String.IsNullOrEmpty(pageFromDb.PageTitle))
                {
                    m_pageService.Save(pageFromDb);
                    m_loggingService.Info("Page saved.");
                    return Json(new {success = true, error = ""});
                }
                else
                    return Json(new {success = false, error = "Unknown error"});
            }
            else
            {
                string errorString = ModelState.Values.SelectMany(v => v.Errors).Aggregate("", (current, error) => current + String.Concat(error.ErrorMessage, "; </BR>"));
                return Json(new {success = false, error = errorString});
            }
        }

        [AcceptVerbs(HttpVerbs.Post)]
        [ValidateInput(false)]
        [CurrentUserFilter]
        public ActionResult SendPage(int id, AdminEditPageViewModel vm, FormCollection forms, string userName)
        {
            if (ModelState.IsValid)
            {
                CmsPage pageFromDb = m_pageService.GetById(id);;
                if (!String.IsNullOrEmpty(pageFromDb.PageTitle))
                {
                    m_pageService.Save(pageFromDb);
                    int? i = id;
                    return Json(new { success = true, error = "", page = i });
                }
                else
                    return Json(new { success = false, error = "Unknown error" });
            }
            else
                return Json(new { success = false, error = "Unknown error" });
        }

        [HttpPost]
        public ActionResult ListPage(int? id, int jtStartIndex = 0, int jtPageSize = 0, string jtSorting = null)
        {
            int count = 0;
            IEnumerable<CmsPage> allPages = m_pageService.LoadAll(id, jtStartIndex, jtPageSize, jtSorting, out count).ToList();
            IEnumerable<CmsPageCategory> allCategories = m_pageService.LoadAllCategories();
            PageListViewModel model = new PageListViewModel();
            model.Pages = allPages;
            model.PageCategories = allCategories;
            return View(model);
        }

        [HttpPost]
        [Authorize]
        public JsonResult Delete(int? Id)
        {
            if (!ModelState.IsValid)
                return Json(new { Result = "ERROR", Message = "", success = false, error = "Unknown error" });

            try
            {
                if (Id != null)
                {
                    m_pageService.Delete(Id.Value);
                    m_loggingService.Warn("Page deleted.");
                    return Json(new { Result = "OK", success = true, error = "", pageId = Id });
                }
                else
                    return Json(new { Result = "ERROR", Message = "", success = false, error = "Unknown error" });
            }
            catch (Exception exc)
            {
                return Json(new { Result = "ERROR", Message = exc.Message, success = false, error = exc.Message });
            }
        }

        [HttpPost, ValidateInput(false)]
        public ActionResult UpdateContent(string content, int id)
        {
            try
            {
                var page = m_pageService.GetById(id);
                page.PageContent = content;
                m_pageService.Save(page);
                return Json(new { result = "ok", success = true, message = "Well done!" });
            }
            catch (Exception ex)
            {
                return Json(new { result = "error", success = false, message = ex.Message });
            }
        }

        #endregion

        #region Tags

        public JsonResult GetTags()
        {
            var tags = m_pageService.GetAllTags().ToList();
            return Json(tags.Select(x => new { x.Name }).Distinct(), JsonRequestBehavior.AllowGet);
        }

        #endregion 

        #region Category

        [HttpPost]
        public JsonResult ListCategories(int jtStartIndex = 0, int jtPageSize = 0, string jtSorting = null)
        {
            try
            {
                //Get data from database
                int categoriesCount = 0;
                List<CmsPageCategory> students = m_pageService.LoadAll(jtStartIndex, jtPageSize, jtSorting, out categoriesCount).ToList();
                return Json(new { Result = "OK", Records = students, TotalRecordCount = categoriesCount });
            }
            catch (Exception ex)
            {
                return Json(new { Result = "ERROR", Message = ex.Message });
            }
        }

        [HttpPost]
        public JsonResult UpdateCategory(CmsPageCategory cat)
        {
            if (!ModelState.IsValid)
                return Json(new { Result = "ERROR", Message = "Form is not valid! Please correct it and try again." });
            try
            {
                cat = m_pageService.UpdateCategory(cat);
                return Json(new { Result = "OK", Record = cat});
            }
            catch (Exception ex)
            {
                return Json(new { Result = "ERROR", Message = ex.Message });
            }
        }

        #endregion

        #region Tree view logic

        private static IEnumerable<NodeViewModel> AddChildNodes(int? parentId, IEnumerable<CmsPageCategory> categories, IEnumerable<CmsPage> pages)
        {
            var gJsTreeArray = categories.Select(item => new NodeViewModel
            {

                data = item.Title,
                state = "closed",
                date = item.CreatedAt.ToString("g"),
                IdServerUse = item.Id, 
                count = "folder",
                icons = "folder", children = null,
                attr = new AttributeViewModel { id = item.Id.ToString(), selected = false, rel = (parentId == null ? "drive" : "folder") }
            }).ToList();

            gJsTreeArray.AddRange(pages.Select(page => new NodeViewModel
            {
                data = page.PageTitle,
                state = "last",
                date = page.CreatedAt.ToString("g"),
                count = "file",
                IdServerUse = page.Id,
                icons = "file",
                children = null,
                attr = new AttributeViewModel { id = page.Id.ToString(), selected = false, rel = "file" }
            }));
            return gJsTreeArray;
        }

        [HttpPost]
        public ActionResult GetAllNodes(int id, string type)
        {
            if (id != -1)
            {
                if (type == "folder" || type == "drive")
                {
                    //TODO: Think on how to retrieve brunch with one query
                    var categories = m_catService.GetAll().Where(x => x.ParentId == id).ToList();
                    var pages = m_pageService.LoadAll().Where(x => x.CategoryId == id).ToList();
                    return Json(AddChildNodes(id, categories, pages));    
                }
                else return Json(new List<NodeViewModel>());
            }

            var gJsTreeArray = new List<NodeViewModel>();
            var rootCategories = m_catService.GetAll().Where(x => x.ParentId == null).ToList();
            gJsTreeArray.AddRange(AddChildNodes(null, rootCategories, new List<CmsPage>()));

            return Json(gJsTreeArray);
        }

        [HttpPost]
        public ActionResult Rename(int id, string type, string newTitle)
        {
            if (id != 0) 
            {
                if (type == "file")
                {
                    var page = m_pageService.LoadAll().FirstOrDefault(x => x.Id == id);
                    page.PageTitle = newTitle;
                    m_pageService.Save(page);
                }
                else
                {
                    var folder = m_pageService.LoadAllCategories().FirstOrDefault(x => x.Id == id);
                    folder.Title = newTitle;
                    m_pageService.UpdateCategory(folder);
                }
            }
            return null;
        }

        #region recursive removal folders and pages
        [HttpPost]
        public ActionResult Remove(int id, string type)
        {
            if (id != 0)
            {
                switch (type)
                {
                    case "file":
                        var file = m_pageService.LoadAll().FirstOrDefault(x => x.Id == id);
                        m_pageService.Delete(file.Id);
                        m_loggingService.Warn("Page deleted.");
                        break;

                    case "folder":
                        RemoveAllFolders(id);
                        m_loggingService.Warn("Folder or branch deleted.");
                        break;

                    default: 
                        break;
                }
                return Json(new { result = "ok", success = true });
            }
            return Json(new { result = "error", success = true });
        }

        private void RemoveAllPagesInFolder(int categoryId)
        {
            var pages = m_pageService.LoadAll().Where(x => x.CategoryId == categoryId);
            var cmsPages = pages as IList<CmsPage> ?? pages.ToList();
            if (cmsPages.Any())
            {
                foreach (var page in cmsPages)
                {
                    m_pageService.Delete(page.Id);
                }
            }

        }

        private void RemoveAllFolders(int id)
        {
            if (id == 0) { return; }
            var category = m_catService.GetAll().FirstOrDefault(x => x.Id == id);
            if (category != null)
            {
                RemoveAllPagesInFolder(category.Id);
                var innerCategories = m_catService.GetAll().Where(x => x.ParentId == id);
                var cmsPageCategories = innerCategories as IList<CmsPageCategory> ?? innerCategories.ToList();
                if (cmsPageCategories.Any())
                {
                    foreach (var item in cmsPageCategories.ToList())
                    {
                        RemoveAllFolders(item.Id);
                    }
                }
                m_pageService.DeleteCategory(category.Id);
            }
        }

        #endregion

        [HttpPost] 
        public ActionResult MoveNode(int id, int parentId, string type)
        {
            var typeNode = type;
            switch (typeNode)
            {
                case "file":
                    var page = m_pageService.LoadAll().Where(x => x.Id == id).FirstOrDefault();
                    page.CategoryId = parentId;
                    m_pageService.Save(page);
                    break;
                case "folder":
                    var folder = m_pageService.LoadAllCategories().Where(x => x.Id == id).FirstOrDefault();
                    folder.ParentId = parentId;
                    m_pageService.UpdateCategory(folder);
                    break;
                default: break;
            }
            return null;
        } 

        [HttpPost]
        public JsonResult AddNode(int parentId, string title, string type)
        {
            int id = 0;
            switch (type)
            {
                case "file":
                    var page = new CmsPage { PageTitle = title, CategoryId = parentId };
                    m_pageService.CreateEntry(page);
                    page.Alias = CreateAliasExtensions.CreateAlias(page.PageTitle, page.Id);
                    page.StatusId = (int) StatusCode.Published;
                    m_pageService.Save(page);
                    m_loggingService.Info("Page created.");
                    id = page.Id;
                    break;

                case "folder":
                    var category = new CmsPageCategory { Title = title, ParentId = parentId };
                    id = m_pageService.CreateCategory(category);
                    m_loggingService.Info("Category created.");
                    break;

                default:
                    break;
            }
            return Json(id);
        }

        #endregion

        #region Internals
        /// <summary>
        /// 
        /// </summary>
        /// <param name="page"></param>
        /// <param name="statusId"></param>
        /// <param name="valueProvider"></param>
        private void UpdatePageStatus(CmsPage page, int statusId, IValueProvider valueProvider)
        {
            page.StatusId = statusId;
            page.Status = null;

            int pageCategoryId;
            if (Int32.TryParse(valueProvider.GetValue("Page.CategoryId").AttemptedValue, out pageCategoryId))
                page.CategoryId = pageCategoryId;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="selStatus"></param>
        /// <returns></returns>
        private SelectList GetStatuses(int? selStatus)
        {
            IEnumerable<CmsStatus> statuses = m_pageService.GetPageStatuses();
            return new SelectList(statuses, "Id", "StatusName", selStatus);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="selStatus"></param>
        /// <returns></returns>
        private SelectList GetCategories(int? selStatus)
        {
            IEnumerable<CmsPageCategory> cats = m_pageService.LoadAllCategories();
            return new SelectList(cats, "Id", "Title", selStatus);
        }
        #endregion

    }
}